#include "../include/FFmpegkits.h"
#include "../include/VedioFrame.h"
FFmpegkits::FFmpegkits(const string url)
    :_url(url)
{
    avformat_network_init();   
}
FFmpegkits::~FFmpegkits()
{
    stop();
}

void FFmpegkits::stop()
{
    if(poutbuff)
    {
    av_free(poutbuff);

    }
    if(avframe)
    {
      av_free(avframe);

    }
    if(avframRGB)
    {
      av_free(avframRGB);

    }
    if(avcodeccontext)
    {
      avcodec_close(avcodeccontext);

    }
    if(avformatcontext)
    {
      avformat_close_input(&avformatcontext);

    }
    if(avpacket)
    {
        av_free(avpacket);
    }
}

void FFmpegkits::start()
{
     char transpottype_key[] = "rtsp_transport";//设置网络传输协议rtsp
      char transportype_value[] = "udp";
      char delay_key[] = "max_delay";
      char delay_value[] = "50000";//设置超时时间
      av_dict_set(&avdic,transpottype_key,transportype_value,0);
      av_dict_set(&avdic, delay_key,delay_value,0);


      //分配格式上下文内存
      avformatcontext = avformat_alloc_context();
      int vedioStreamIndex = -1;

      //初始化ffmpeg
        avformat_network_init();
      //打开rtsp视频
      if(avformat_open_input(&avformatcontext,_url.c_str(),NULL,&avdic) != 0)
      {
          printf("cant open rtsp file\n");
          return;
      }
      //查看一下流的个数
      cout << "number of the stream : " << avformatcontext->nb_streams << endl;
      //分析视频流
      if(avformat_find_stream_info(avformatcontext,NULL) != 0)
      {
          printf("cant find stream info\n");
          return;
      }
      //音视频封装上下文结构体内的streams数组存放了多种流，所以要寻找到最好的流，每个流对应一个解码器
      vedioStreamIndex = av_find_best_stream(avformatcontext,AVMEDIA_TYPE_VIDEO, -1,-1,nullptr,0);
      if(vedioStreamIndex < 0)
      {
          printf("cant find best stream\n");
          return;
      }

      //查看解码器，也可以直接在find_best_stream函数内直接传入传出一个解码器，便可以获取对应的解码器
      if((avcodec = avcodec_find_decoder(avformatcontext->streams[vedioStreamIndex]->codecpar->codec_id)) == nullptr)//寻找特定解码器
      {
          printf("cant find the decoder\n");
          return;
      }
      //开辟解码器上下文空间
      avcodeccontext = avcodec_alloc_context3(avcodec);
      //拷贝解码器参数，根据流会获得对应的解码器，而解码器参数则存放在音视频上下文结构体内的codexpar内，而解码则需要
      //解码器上下文结构体进行解码，所以要复制进去
      if(avcodec_parameters_to_context(avcodeccontext,avformatcontext->streams[vedioStreamIndex]->codecpar) < 0)
      {
         av_log(avcodeccontext,AV_LOG_ERROR,"cant copy parameter to contex");
         return;
      }
      cout << "avcodeccontex->codec_id" << avcodeccontext->codec_id << endl;
      //初始化解码器参数,通常先从上下文进行复制，再根据不同的场景进行调整
      //avcodeccontext->bit_rate = 0;
      avcodeccontext->time_base.num = 1;
      avcodeccontext->time_base.den= 25;
      //avcodeccontext->frame_number = 1; 自动管理，不建议手动配置

      //打开解码器,
      if(avcodec_open2(avcodeccontext,avcodec,nullptr) < 0)
      {
          printf("cant open avcodec\n");
          return;
      }

      //进行格式转换上下文，将YUV格式转换成RGB32形式，及压缩格式转换成高精度像素格式，相同宽高，即分辨率不变，但也可以进行转换
      //这里我改了算法，使用的是最近临插法，会降低分辨率但是实时性非常高
      pImageConver = sws_getContext(avcodeccontext->width,avcodeccontext->height,avcodeccontext->pix_fmt,
                                    avcodeccontext->width,avcodeccontext->height,AV_PIX_FMT_RGB32,SWS_POINT,
                                    nullptr,nullptr,nullptr);

      //计算一帧RGB32帧所需要的字节
      int numBytes = avpicture_get_size(AV_PIX_FMT_RGB32, avcodeccontext->width, avcodeccontext->height);
      //分配原始帧和RGB帧内存 + 缓冲区
      avframe = av_frame_alloc();
      avframRGB = av_frame_alloc();
      poutbuff = (uint8_t*)av_malloc(numBytes*sizeof(uint8_t));
      //将输出缓冲区和RGB帧关联
      avpicture_fill((AVPicture*)avframRGB,poutbuff,AV_PIX_FMT_RGB32,avcodeccontext->width, avcodeccontext->height);
      //分配并初始化数据包
      avpacket = (AVPacket*)malloc(sizeof(AVPacket));//这个packet是存储压缩格式的数据结构
      int y_size = avcodeccontext->width*avcodeccontext->height;
      av_new_packet(avpacket,y_size);


      //主播放循环
      while(1)
      {

          //读取数据包
          if(av_read_frame(avformatcontext,avpacket))
          {
              break;
          }
          if(avpacket->stream_index == vedioStreamIndex)
          {
              int got_picture;
              int ret = avcodec_decode_video2(avcodeccontext,avframe,&got_picture,avpacket);
              if(ret < 0)
              {
                  printf("cant decode vedio");
                  return;
              }
              if(got_picture)
              {
                  //执行转换
                  sws_scale(pImageConver,( uint8_t const * const *)avframe->data,avframe->linesize,0,avcodeccontext->height,
                            avframRGB->data,avframRGB->linesize);
                  //将数据转换成qimage
                  QImage tmpImg((uchar *)poutbuff,
                                avcodeccontext->width,avcodeccontext->height,
                                QImage::Format_RGB32);
                  QImage image =  tmpImg.copy();
                  emit sigGetOneImage(image);
              }

          }
          av_free_packet(avpacket);
      }

}
